package com.emmmya.utils.exprossion;

import com.emmmya.annotation.ExpressionMethod;
import com.emmmya.common.CommonPattern;
import com.emmmya.exception.ExpressionExcption;
import com.emmmya.function.BuiltInFunction;
import com.emmmya.executor.DefaultExecutor;
import com.emmmya.harinCheck.annotation.Check;
import com.emmmya.harinCheck.engine.AbsHarinCheckEngine;
import com.emmmya.harinCheck.utils.EnumGetValueUtils;
import com.emmmya.harinCheck.utils.HarinStringUtils;
import com.emmmya.harinCheck.utils.RegexUtils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @author Harin
 * @version 1.0
 * @date 2022-05-06 16:35:22
 */
public class ExprossionUtils {

    static String LAST_FIELD_NAME = "lastFieldName";

    static String FITLER_FUN_LIST = "fitlerFunList";

    static String SINGLE_PATTERN = "\\'[^\\']*\\'";

    //获取函数的参数
    static String PARAMETER_PATTERN = "\\(([\\S\\s\\(\\)])*\\)$";

    //系统通用内置函数
    static Map<String, Method> BUILT_IN_METHOD = BuiltInFunction.METHOD_NAME_MAP;

    //for循环内部结构
    public static String FOR_FUNCTION_PARAM = "\\sin\\s";



    /**
     * 推荐直接调用该方法获取属性方法，内置了根据map和普通实体对象方式来获取
     */
    public static Object getExprossionValue(Object source,String fieldName){
        return getExprossionValue(source,fieldName,null);
    }


    /**
     * 推荐直接调用该方法获取属性方法，内置了根据map和普通实体对象方式来获取
     */
    public static Object getExprossionValue(Object source,String fieldName,Map<String,Method> methodsMap){
        //1.拆分属性名，有可能是属于链式编程属性 如 角色.权限.权限代码
        List<String> chainFieldList = splitChainField(fieldName);
        //2.如果是链式属性，则使用拆分属性方法获取属性值
        if(chainFieldList!= null && chainFieldList.size()>0){
            return getFieldValueByChain(source,fieldName,methodsMap);
        }
        //2.如果不是链式属性，则调用直接获取属性值方法
        return getFieldValue(source,fieldName);
    }

    /**
     * 获取对象 指定属性名的 值 只能获取非链式编程的数据
     * @param colName       字段属性名
     * @param source        源对象
     * @return              如果存在属性值，则返回，不存在则返回null
     */
    public static Object getFieldValue(Object source,String colName){
        try {
            //如果只有一个 单引号，则认为是一个字符串
            List<String> matcherList = RegexUtils.getMatcherList("^" + SINGLE_PATTERN + "$", colName.trim());

            if (matcherList.size() == 1) {
                String colNameTrim = colName.trim();
                String substring = colNameTrim.substring(1, colNameTrim.length() - 1);
                return substring;
            }
            //判断是否为数字
            String numberPatternStr = CommonPattern.NUMBER.getPatternStr();
            Pattern compile = Pattern.compile(numberPatternStr);
            if (RegexUtils.findAny(compile, colName)) {
                return colName;
            }

            if (Map.class.isInstance(source)) {
                return getFieldValue((Map<String, ? extends Object>) source, colName);
            }
            String[] colNames = colName.split("\\.");
            if (!HarinStringUtils.isEmpty(colNames[0])){
                //查询是否存在该属性名的字段
                Field field = getField(source, colNames[0]);
                if (field != null) {
                    Check check = field.getAnnotation(Check.class);
                    Class<?> enumClass = check.enumValues();
                    field.setAccessible(true);
                    //如果calName等于传入的oneExp，则直接取值
                    Object target = null;
                    if (colNames.length > 1) {
                        target = field.get(source);
                        Field finalField = null;
                        for (int i = 1; i < colNames.length; i++) {
                            String colName1 = colNames[i];
                            finalField = getField(target, colName1);
                            finalField.setAccessible(true);
                            target = finalField.get(target);
                        }
                    } else {
                        target = field.get(source);
                    }
                    //只要注解中的类不是Check
                    if(!enumClass.isInstance(check)){
                        target = EnumGetValueUtils.getEnumValue(enumClass,target);
                    }
                    return target;
                }
            }
        }catch(Exception e){
            e.printStackTrace();
        }
        //不管有没有异常，都需要有返回值，所以最终给个null，使用时候就需要自己判断
        return null;
    }



    /**
     * 根据类名获取枚举的键值集合
     * @param enumClass
     * @return 枚举的间值map
     * @throws NoSuchMethodException
     */
    private static Map<Object, Object> getEnumMap(Class<?> enumClass){
        try {
            Method getDataMapMethod = enumClass.getDeclaredMethod("getDataMap");
            Map<Object, Object> o = (Map<Object, Object>)DefaultExecutor.executeMethod(getDataMapMethod, null);
            return o;
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 如果是map类型的数据，则直接根据字段名作为key，返回对应的value
     * @param dataMap   源数据
     * @param key       key值（作为属性名）
     * @return
     */
    public static Object getFieldValue(Map<String,? extends Object> dataMap,String key){
        return dataMap.get(key);
    }

    /**
     * 介绍：用户.名称，需要切割好List<String> 用户、名称，按照顺序排好
     * @param dataMap       数据源
     * @param keys          链式属性名
     * @return
     */
    public static Object getFieldValue(Map<String,? extends Object> dataMap,List<String> keys){
        if(dataMap == null) return null;
        Object o = dataMap.get(keys.get(0));
        Object temp = o;
        for (int i = 1; i < keys.size(); i++) {
            temp = getFieldValue(temp, keys.get(i));
        }
        return temp;
    }

    /**
     * 获取链式属性、或调用了过滤器的属性值
     * @param source
     * @param colName
     * @param methodsMap 函数集合
     * @return
     */
    public static Object getFieldValueByChain(Object source,String colName,Map<String,Method> methodsMap){
        try {
            if(Map.class.isInstance(source)){
                return getFieldValueByChain((Map<String, ? extends Object>) source,colName,methodsMap);
            }

            //如果只有一个 单引号，则认为是一个字符串
            List<String> matcherList = RegexUtils.getMatcherList("^"+SINGLE_PATTERN+"$", colName.trim());

            if(matcherList.size() == 1){
                String trim = colName.trim();
                return trim.substring(1,trim.length()-1);
            }


            List<String> fieldList = splitChainField(colName);
            Object temp = source;

            String lastFieldName = fieldList.get(fieldList.size() - 1);
            Map<String, Object> filterFunctionMap = splitFilterFunction(lastFieldName);

            String o = (String)filterFunctionMap.get(LAST_FIELD_NAME);
            if(!HarinStringUtils.isEmpty(o)){
                fieldList.set(fieldList.size() - 1,o);
            }

            List<String> filterFunList = (List<String>)filterFunctionMap.get(FITLER_FUN_LIST);

            //处理最后一个属性名之前的属性。
            for (int i = 0; i < fieldList.size() -1; i++) {
                String fieldName = fieldList.get(i);
                if(temp == null) {return null;}
                temp = getFieldValue(temp, fieldName);
            }

            //执行过滤器函数
            if(filterFunList!= null){
                temp = getFieldValue(temp, o);
                Object paramTempObj = temp;
                for (String filterFunStr : filterFunList) {
                    List<Object> filterFunParams = getFilterFunParams(paramTempObj,source , filterFunStr,",");
                    //拼接参数
                    ArrayList<Object> params = new ArrayList<>();
                    params.add(paramTempObj);
                    params.addAll(params.size(),filterFunParams);
                    Method method = matchingFunction(methodsMap, filterFunStr, params);
                    if(method != null) {
                        temp = DefaultExecutor.executeMethod(method, params.toArray());
                    }
                }
            }
            //不是过滤器，而是字符拼接
            else if(lastFieldName.contains("+")){
                temp = strSplicing(temp, lastFieldName, methodsMap);
            }
            //如果啥都不是，那就获取最后一个属性的值。
            else if(temp != null){
                temp = getFieldValue(temp, o);
            }

            return temp;
        }catch(Exception e){ e.printStackTrace(); }
        //不管有没有异常，都需要有返回值，所以最终给个null，使用时候就需要自己判断
        return null;
    }


    /**
     * 如果是map类型的数据，则直接根据字段名作为key，返回对应的value
     * 链式属性
     * @param dataMap   数据源
     * @param key       需要获取的属性
     */
    public static Object getFieldValueByChain(Map<String,? extends Object> dataMap,String key,Map<String,Method> methodsMap){
        try {
            List<String> fieldList = splitChainField(key);
            //获取最后一个属性
            String lastFieldName = fieldList.get(fieldList.size() - 1);
            //判断最后一个属性，是否存在过滤器
            Map<String, Object> filterFunctionMap = splitFilterFunction(lastFieldName);

            fieldList.set(fieldList.size() - 1, (String) filterFunctionMap.get(LAST_FIELD_NAME));
            Object temp = getFieldValue(dataMap, fieldList);
            if (filterFunctionMap.containsKey(FITLER_FUN_LIST)) {
                List<String> filterFunList = (List<String>) filterFunctionMap.get(FITLER_FUN_LIST);
                Object paramTempObj = temp;
                for (String filterFunStr : filterFunList) {
                    //Method method = matchingFunction(methodsMap, filterFunStr);
                    List<Object> filterFunParams = getFilterFunParams(paramTempObj, dataMap, filterFunStr,",");
                    ArrayList<Object> params = new ArrayList<>();
                    params.add(paramTempObj);
                    params.addAll(params.size(), filterFunParams);
                    Method method = matchingFunction(methodsMap, filterFunStr,params);
                    if (method != null) {
                        temp = DefaultExecutor.executeMethod(method, params.toArray());
                    }
                }
            }
            return temp;
        }catch (Exception e){e.printStackTrace();}
        return null;
    }


    /**
     * 获取调用的过滤器函数中的参数       如 toNumnber(保留几位小数位)
     * @param paramTempObj          当前调用过滤器函数的第一个参数，也就是第一个 | 前的参数
     * @param source                最原始的待处理对象
     * @param filterFunStr          当前的过滤器函数（字符串类型，包含参数）
     * @return
     */
   public static List<Object> getFilterFunParams(Object paramTempObj,Object source, String filterFunStr,String separator) {
        //正则匹配公式小括号信息
        Pattern compile = Pattern.compile(PARAMETER_PATTERN);
        Matcher matcher = compile.matcher(filterFunStr);
        //如果有小括号
        if(matcher.find()) {
            String group = matcher.group();
            //获取内容参数
            String param = group.substring(1, group.length() - 1);
            List<Object> objects = matchingParams(paramTempObj,source, param,separator);
            return objects;
        }
        return new ArrayList<Object>();
    }

    /**
     * 匹配参数
     * @param paramTempObj      当前调用过滤器函数的第一个参数，也就是第一个 | 前的参数
     * @param source            最原始的待处理对象
     * @param paramStr          过滤器函数中参数
     * @return
     */
    private static List<Object> matchingParams(Object paramTempObj,Object source,String paramStr,String separator){
        if(HarinStringUtils.isBank(paramStr)){
            return null;
        }
        //正则匹配公式小括号信息
        Pattern compile = Pattern.compile(SINGLE_PATTERN);
        Matcher matcher = compile.matcher(paramStr);
        String tempStr = String.copyValueOf(paramStr.toCharArray());
        Map<String, String> tempStrParamMap = new HashMap<>();
        int strParamCount = 0;
        while (matcher.find()){
            strParamCount++;
            String key = "\"strParam"+strParamCount+"\"";
            String group = matcher.group();
            tempStr = tempStr.replace(group, key);
            //存入临时变量池，这里需要去掉单引号
            tempStrParamMap.put(key,group.substring(1,group.length()-1));
        }
        String[] paramArray = tempStr.split(separator);
        List<Object> params = new ArrayList<>();
        for (int i = 0; i < paramArray.length; i++) {
            String param = paramArray[i];
            if(tempStrParamMap.containsKey(param)){
                params.add(tempStrParamMap.get(param));
            }else{
                //先判断是否为数字
                boolean any = RegexUtils.findAny(AbsHarinCheckEngine.NUMBER_PATTERN,param);
                if(any){
                    BigDecimal paramBigTemp = new BigDecimal(param);
                    if(param.contains(".")) {
                        params.add((Double)paramBigTemp.doubleValue());
                    }else{
                        params.add((Integer)paramBigTemp.intValue());
                    }
                }else if("true".equalsIgnoreCase(param)){
                    params.add(true);
                }else if("false".equalsIgnoreCase(param)){
                    params.add(false);
                }else {
                    //Object fieldValue = getFieldValue(source, param);
                    params.add(getFieldValue(source, param));

                }
            }
        }
        return params;

    }


    /**
     * 全局匹配：先匹配传入的函数集，在判断系统内置函数集
     * 匹配函数
     * @param methodsMap        待匹配的传入的函数集
     * @param filterFunStr      函数字符串
     * @return
     */
//    static Method matchingFunction(Map<String, Method> methodsMap, String filterFunStr) {
//        //先判断传入的内置函数集合
//        Method method = matchFunction(filterFunStr, methodsMap);
//        //再判断系统的内置函数集合
//        if(method == null){
//            method = matchFunction(filterFunStr,BUILT_IN_METHOD);
//        }
//        return method;
//    }

    /**
     * 全局匹配：先匹配传入的函数集，在判断系统内置函数集
     * 匹配函数
     * @param methodsMap        待匹配的传入的函数集
     * @param filterFunStr      函数字符串
     * @param params            参数
     * @return
     */
    public static Method matchingFunction(Map<String, Method> methodsMap, String filterFunStr,List<Object> params) {
        //先判断传入的内置函数集合
        Method method = matchFunction(filterFunStr, methodsMap,params);
        //再判断系统的内置函数集合
        if(method == null){
            method = matchFunction(filterFunStr,BUILT_IN_METHOD,params);
        }
        return method;
    }

    /**
     * 根据属性名称，获取字段
     * @param obj           对象
     * @param colName       字段名称 @Check的name属性
     * @return
     */
    public static Field getField(Object obj, String colName){
        if(HarinStringUtils.isEmpty(colName)) throw new ExpressionExcption("属性名不允许为空");
        if(HarinStringUtils.isEmpty(obj)) return null;
        Class<?> aClass = obj.getClass();
        Field[] allFields = getAllFields(aClass);
        List<Field> fields = Arrays.stream(allFields).filter(f ->{
            Check annotation = f.getAnnotation(Check.class);
            if(annotation != null) {
                return colName.equals(annotation.name());
            } return false;
        }).collect(Collectors.toList());

        if(fields == null || fields.size() ==0 ){
            return null;
        }
        if(fields.size() > 1){
            throw new ExpressionExcption("存在多个相同属性名的属性！");
        }
        return fields.get(0);

    }

    /**
     * 去除{{}},去除两边空格
     */
    public static String removeCurlyBracket(String expression){
        if(HarinStringUtils.isEmpty(expression)){
            return expression;
        }
        if(expression.startsWith("{{")){
            expression = expression.substring(2,expression.length());
            if(expression.endsWith("}}")){
                expression = expression.substring(0,expression.lastIndexOf("}}"));
            }
        }
        return expression.trim();
    }

    /**
     * 根据传入的函数集合匹配名为functionName的函数
     * @param   functionName        需要获取的函数名
     * @param   methodPatternMap    待匹配的函数集合
     * @return Method 匹配的函数，不存在则返回null
     */
    public static Method matchFunction(String functionName, Map<String, Method> methodPatternMap,List<Object> params){
        if(functionName.startsWith("{{") && functionName.endsWith("}}")){
            functionName = removeCurlyBracket(functionName).trim();
        }
        Set<String> keySet = methodPatternMap.keySet();
        A:for (String methodPatten : keySet) {
            Pattern compile = Pattern.compile(methodPatten);
            Matcher matcher = compile.matcher(functionName);
            if(matcher.find()){ //如果找到了函数，则返回函数
                Method method = methodPatternMap.get(methodPatten);
                ExpressionMethod annotation = method.getAnnotation(ExpressionMethod.class);
                if(annotation!= null){
                    String before = annotation.before();
                    if(!before.equals(functionName) && HarinStringUtils.isNotEmpty(before)) {
                        method = matchFunction(before, methodPatternMap, params);
                        return method;
                    }
                }
                Class<?>[] parameterTypes = method.getParameterTypes();
                if(params.size() != parameterTypes.length){
                    continue A;
                }else{
                    for (int i = 0; i < params.size(); i++) {
                        //参数
                        Object param = params.get(i);
                        //参数类型
                        Class<?> parameterType = parameterTypes[i];
//                        System.out.println(method.getName());
                        //如果参数不为空，并判断参数的类型是否能匹配的上
                        if(param != null && !parameterType.isAssignableFrom(param.getClass()) ){
                            continue A;
                        }
                    }
                    return method;
                }
            }
        }

        return null;
    }

    /**
     * 链式属性处理，根据.隔开
     * @param field 链式属性的字符串
     * @retrun 根据.切割后的数组
     */
    public static List<String> splitChainField(String field){
        if(field.contains(".")){
            String[] split = field.split("\\.");
            return Arrays.asList(split);
        }
        List<String> chainFieldList = new ArrayList<>();
        chainFieldList.add(field);
        return chainFieldList;
    }

    /**
     * 是否包含过滤器
     * @param field     带判断的属性
     * @return
     */
    public static Boolean containFilter(String field){
        if(HarinStringUtils.isEmpty(field) || !field.contains("|")) {
            return false;
        }
        return true;
    }


    /**
     * 切割过滤器
     * @param field     包含过滤器的属性       如： 数量 | toNumber(3) | default('空') | ...
     * @return map      key为LAST_FIELD_NAME的为 属性名（上面列子的 “数量”），
     *                  keyFITLER_FUN_LIST的为 待调用过滤器集合（上面例子的 toNumber(3)、default('空')...）
     */
    public static Map<String,Object> splitFilterFunction(String field){
        HashMap<String, Object> result = new HashMap<>();
        if(!containFilter(field)) {
            result.put(LAST_FIELD_NAME,field.trim());
            return result;
        }
        String[] split = field.split("\\|");
        String s = split[0];
        result.put(LAST_FIELD_NAME,s.trim());
        List<Object> filterFunList = new ArrayList<>();
        Pattern param = Pattern.compile(PARAMETER_PATTERN);

        StringBuilder sbTemp = new StringBuilder();
        //避免有参数使用 | 当作参数传入，需要拼接过滤器完整性
        for (int i = 1; i < split.length; i++) {
            String filterFun = split[i].trim();
            //如果能正常的匹配到参数 (xx) ，则加入过滤器集合中
            if(RegexUtils.findAny(param,filterFun)) {
                filterFunList.add(filterFun);
            }else{
                if(i != split.length-1) {
                    sbTemp.append(split[i] + "|");
                }else{
                    sbTemp.append(split[i]);
                }
                if(RegexUtils.findAny(param,sbTemp.toString().trim())){
                    //拼接回|符号
                    filterFunList.add(sbTemp.toString().trim());
                    sbTemp.delete(0,sbTemp.length());
                }
            }
        }
        result.put(FITLER_FUN_LIST,filterFunList);
        return result;
    }


    /**
     * 字符串拼接， 判断 拼接情况
     * @param obj               源数据
     * @param colName           属性值（包含拼接情况）
     * @param methodMap         过滤器函数
     * @return
     */
    public static Object strSplicing(Object obj, String colName,Map<String,Method> methodMap){
        if(obj == null){return obj;}
        StringBuilder resultStr = new StringBuilder();
        String tempStr = String.copyValueOf(colName.toCharArray());
        List<String> matcherList = RegexUtils.getMatcherList(SINGLE_PATTERN, colName);
        Map<String,String> tempStrParam = new LinkedHashMap();
        if(matcherList!= null && matcherList.size() > 0){
            for (int i = 0; i < matcherList.size(); i++) {
                String s = matcherList.get(i);
                tempStr = tempStr.replace(s, "SStr"+i+"trSS");
                tempStrParam.put("SStr"+i+"trSS",s.substring(1,s.length()-1));
            }
        }
        if(tempStr.indexOf("+") >= 0) {
            String[] split = tempStr.split("\\+");
            for (int i = 0; i < split.length; i++) {
                String fieldName = split[i].trim();
                if (tempStrParam.containsKey(fieldName)) {
                    resultStr.append(tempStrParam.get(fieldName));
                } else {
                    //如果有函数就分割
                    Object fieldValue = getFieldValueByChain(obj, fieldName, methodMap);
                    resultStr.append(fieldValue);
                }
            }
        }else{
            return obj;
        }
        return resultStr.toString();


    }

    /**
     * 获取实体类的所有属性
     * @param obj
     * @return
     */
    public static Field[] getAllFields(Object obj) {
        return obj == null ? null : getAllFields(obj.getClass());
    }
    /**
     * 获取字节码对象的所有属性
     * @param clazz
     * @return
     */
    public static Field[] getAllFields(Class<?> clazz) {
        ArrayList fieldList;
        for(fieldList = new ArrayList(); clazz != null; clazz = clazz.getSuperclass()) {
            fieldList.addAll(new ArrayList(Arrays.asList(clazz.getDeclaredFields())));
        }
        Field[] fields = new Field[fieldList.size()];
        return (Field[])fieldList.toArray(fields);
    }

}
